package org.contextsensitivetodo.context.rules.location;

import org.contextsensitivetodo.context.ContextDelivery;
import org.contextsensitivetodo.context.rules.SuperContextRule;

import android.location.Location;
import android.location.LocationManager;
import android.util.Log;

/**
 * This class is location context rule. This rule is active if the defined
 * location context is true
 * 
 * @author juhani
 * 
 */
public class LocationContextRule extends SuperContextRule {

	private Location location;
	private int distanceAllowedinMeters;

	private boolean isNegative;

	private Location debugLastGPSLocation;
	private Location debugLastNetworkLocation;
	private boolean debugLastEvaluationWasDoneUsingGPS = false;
	private int debugLastDistanceAllowed;

	public LocationContextRule(Location location, int distanceAllowedinMeters,
			boolean isNegative) {
		super();
		this.location = location;
		this.distanceAllowedinMeters = distanceAllowedinMeters;
		this.isNegative = isNegative;
	}

	
	public void doStateUpdate(Location newLocation) {
		Location setLocation = new Location(this.location);
		if(LocationManager.NETWORK_PROVIDER.equals(newLocation.getProvider())){// network location doesn't have altitude...
			// do nothing for now
			setLocation.setAltitude(0);
			this.debugLastNetworkLocation = newLocation;
			this.debugLastEvaluationWasDoneUsingGPS = false; 
		}else{
			this.debugLastGPSLocation = newLocation;
			this.debugLastEvaluationWasDoneUsingGPS = true;
		}
		
		Log.d("cstodo", "Last known location: " + newLocation);
		Log.d("cstodo", "Distance to setup location: "
				+ setLocation.distanceTo(newLocation));

		int distanceAllowed = this.distanceAllowedinMeters + (int)newLocation.getAccuracy();
		this.debugLastDistanceAllowed = distanceAllowed;
		
		if (setLocation.distanceTo(newLocation) <= distanceAllowed) {
			if (isNegative) {
				this.setNewState(false);
			} else {
				this.setNewState(true);
			}

		} else {
			if (isNegative) {
				this.setNewState(true);
			} else {
				this.setNewState(false);
			}
		}
		                                                                      
	}
	
	
	/**
	 * This should not be used. This implementation sucks!
	 */
	public void doStateUpdate() {
		Log.d("cstodo", "Starting location rule update");

		try {
			
			Location lastKnownNetworkLocation = ContextDelivery.getInstace()
					.getLocationManager().getLastKnownLocation(
							LocationManager.NETWORK_PROVIDER);
			Location lastKnownGPSLocation = ContextDelivery.getInstace()
					.getLocationManager().getLastKnownLocation(
							LocationManager.GPS_PROVIDER);

			Log.d("cstodo", "GPS location: " + lastKnownGPSLocation);
			Log.d("cstodo", "network location: " + lastKnownNetworkLocation);

			if (lastKnownGPSLocation == null
					&& lastKnownNetworkLocation == null) {
				if (isNegative) {
					this.setNewState(true);
				} else {
					this.setNewState(false);
				}
				return;
			}

			Location lastKnownLocation;

			Location setLocation = new Location(this.location);
			if (lastKnownGPSLocation == null) {
				lastKnownLocation = lastKnownNetworkLocation;
				this.debugLastEvaluationWasDoneUsingGPS = false;
				this.debugLastNetworkLocation = lastKnownNetworkLocation;
				setLocation.setAltitude(0);
			} else if (lastKnownNetworkLocation == null) {
				lastKnownLocation = lastKnownGPSLocation;
				this.debugLastGPSLocation = lastKnownGPSLocation;
				this.debugLastEvaluationWasDoneUsingGPS = true;
			} else {
				this.debugLastNetworkLocation = lastKnownNetworkLocation;
				this.debugLastGPSLocation = lastKnownGPSLocation;

				if (lastKnownGPSLocation.getTime() > lastKnownNetworkLocation
						.getTime()) {
					lastKnownLocation = lastKnownGPSLocation;
					this.debugLastEvaluationWasDoneUsingGPS = true;
				} else {
					lastKnownLocation = lastKnownNetworkLocation;
					this.debugLastEvaluationWasDoneUsingGPS = false;
					setLocation.setAltitude(0);
				}
			}

			Log.d("cstodo", "Last known location: " + lastKnownLocation);
			Log.d("cstodo", "Distance to setup location: "
					+ setLocation.distanceTo(lastKnownLocation));

			int distanceAllowed = this.distanceAllowedinMeters + (int)lastKnownLocation.getAccuracy();
			this.debugLastDistanceAllowed = distanceAllowed;
			
			if (setLocation.distanceTo(lastKnownLocation) <= distanceAllowed) {
				if (isNegative) {
					this.setNewState(false);
				} else {
					this.setNewState(true);
				}

			} else {
				if (isNegative) {
					this.setNewState(true);
				} else {
					this.setNewState(false);
				}
			}
		} catch (Exception e) {
			Log.e("cstodo", "Exception updating location state " + e);
			e.printStackTrace();
		}
	}

	public String getRuleName() {

		StringBuffer sb = new StringBuffer("LocationContextRule");
		if(this.isNegative){
			sb.append(" - negative");
		}
		sb.append("\n");
		sb.append("Set location: " + this.location.getLatitude() + " - "
				+ this.location.getLongitude() + " alt: "
				+ this.location.getAltitude() + "\n");

		if (this.debugLastGPSLocation != null) {
			sb.append("Last GPS location: "
					+ this.debugLastGPSLocation.getLatitude() + " - "
					+ this.debugLastGPSLocation.getLongitude() + " alt: "
					+ this.debugLastGPSLocation.getAltitude() + "\n"+ "accuracy: " + this.debugLastGPSLocation.getAccuracy()+"\n");
			sb.append("  was updated (mins ago): "
					+ ((System.currentTimeMillis() - this.debugLastGPSLocation
							.getTime()) / 60000));
			
			sb.append("\nDistance from set location is: "+this.debugLastGPSLocation.distanceTo(this.location)+"\n");
		} else {
			sb.append("GPS location null\n");
		}

		if (this.debugLastNetworkLocation != null) {
			sb.append("Last Network location: "
					+ this.debugLastNetworkLocation.getLatitude() + " - "
					+ this.debugLastNetworkLocation.getLongitude() + " alt: "
					+ this.debugLastNetworkLocation.getAltitude() + "\n" + "accuracy: " + this.debugLastNetworkLocation.getAccuracy()+"\n");
			sb
					.append("  was updated (mins ago): "
							+ ((System.currentTimeMillis() - this.debugLastNetworkLocation
									.getTime()) / 60000));
			Location temp = new Location(this.location);
			temp.setAltitude(0);
			sb.append("\nDistance from set location is: "+this.debugLastNetworkLocation.distanceTo(temp)+"\n");
		} else {
			sb.append("Network location null\n");
		}

		sb.append("Last update was done using GPS: "+this.debugLastEvaluationWasDoneUsingGPS+"\ndistance allowed was: "+this.debugLastDistanceAllowed+"\n\n");
		
		return sb.toString();
	}

}
